package com.ticket.sass.provider.handler;


import com.ticket.sass.common.exception.enums.ErrorCode;
import com.ticket.sass.common.exception.exception.BaseException;
import com.ticket.sass.provider.model.ResultModel;
import lombok.extern.slf4j.Slf4j;
import org.mybatis.spring.MyBatisSystemException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.multipart.MultipartException;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * Controller层全局异常捕获
 * @author ywb
 * @date 2021-10-08 09:24
 */
@Slf4j
@RestControllerAdvice(basePackages = "com.ticket.sass.provider.controller")
public class GlobalExceptionHandler {
    String field;
    String value;
    String message;
    String fieldType;

    @ExceptionHandler({
        BindException.class,
        ConstraintViolationException.class,
        MultipartException.class,
        MethodArgumentTypeMismatchException.class,
        MethodArgumentNotValidException.class
    })
    public ResultModel<String> methodArgumentNotValidExceptionHandler(Exception e) {
        if (e instanceof MethodArgumentNotValidException) {
            MethodArgumentNotValidException ex = (MethodArgumentNotValidException)e;
            log.error(
                "参数校验异常，msg: 【{}】",
                ex.getBindingResult().getAllErrors().get(0).getDefaultMessage()
            );
            if (Objects.isNull(ex.getBindingResult().getFieldError())) {
                field = ex.getBindingResult().getAllErrors().get(0).getObjectName();
            } else {
                field = ex.getBindingResult().getFieldError().getField();
            }
            message = ex.getBindingResult().getAllErrors().get(0).getDefaultMessage();
            return new ResultModel<>(
                ErrorCode.BAD_REQUEST.getValue(),
                String.format("错误来源字段：【%s】, 错误字段提示【%s】", field, message)
            );
        }
        if (e instanceof ConstraintViolationException) {
            ConstraintViolationException ex = (ConstraintViolationException)e;
            message = ex.getConstraintViolations().stream().map(ConstraintViolation::getMessage).collect(Collectors.joining());
            log.error(
                "参数校验异常，msg: 【{}】",
                message
            );

            return new ResultModel<>(
                ErrorCode.BAD_REQUEST.getValue(),
                String.format("错误字段提示【%s】", message)
            );
        }

        if (e instanceof BindException){
            BindException ex = (BindException)e;
            field = !Objects.isNull(ex.getBindingResult().getFieldError()) ? ex.getBindingResult().getFieldError().getField() : "";
            log.error("普通参数输入不匹配错误，msg:【{}】", ex.getBindingResult().getFieldError());
            return new ResultModel<>(
                ErrorCode.BAD_REQUEST.getValue(),
                String.format("错误来源字段：【%s】", field)
            );
        }
        if (e instanceof MethodArgumentTypeMismatchException) {
            MethodArgumentTypeMismatchException ex = (MethodArgumentTypeMismatchException)e;
            log.error("方法级别类型错误，msg:【{}】", ex.getMessage());
            field = ex.getName();
            value = ex.getValue() + "";
            fieldType =  !Objects.isNull(ex.getRequiredType()) ? ex.getRequiredType().getSimpleName() : "";
            return new ResultModel<>(
                ErrorCode.BAD_REQUEST.getValue(),
                String.format("参数【%s】 提交的参数值：【%s】错误, 需要的参数类型：【%s】", field, value, fieldType)
            );
        }
        if (e instanceof MultipartException) {
            MultipartException ex = (MultipartException)e;
            log.error("文件上传错误，msg:【{}】", ex.getMessage());
            message = "文件上传错误";
            return new ResultModel<>(
                ErrorCode.BAD_REQUEST.getValue(),
                message
            );
        }

        return new ResultModel<>(
            ErrorCode.BAD_REQUEST.getValue(),
            ErrorCode.BAD_REQUEST.getReasonPhrase()
        );
    }

    /**
     * 捕获特殊异常
     */
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public ResultModel<String> httpMessageNotReadableException(
        HttpMessageNotReadableException ex
    ) {
        String message = "入参错误，请检查JSON格式";
        log.error("特殊异常，msg:【{}】", ex.getMessage());

        return new ResultModel<>(
            ErrorCode.CLIENT_ERROR.getValue(),
            message
        );
    }

    /**
     * 捕获全局自定义异常
     */
    @ResponseBody
    @ExceptionHandler(BaseException.class)
    public ResultModel<String> baseException(BaseException e) {
        // 打印错误日志
        log.error("自定义错误代码({}),错误信息({})", e.getErrorCode(), e.getErrorMessage());
        return new ResultModel<>(
            e.getErrorCode().getValue(),
            e.getErrorMessage()
        );
    }


//    /**
//     * 捕获意料之外的异常Exception
//     */
//    @ResponseBody
//    @ExceptionHandler(Exception.class)
//    public ResultModel<String> otherErrorDispose(Exception e) {
//        // 打印错误日志
//        log.error("系统异常错误代码({}),错误信息({})", 500, e);
//        return new ResultModel<>(
//            ErrorCode.SERVER_ERROR.getValue(),
//            ErrorCode.SERVER_ERROR.getReasonPhrase()
//        );
//    }
    
    @ResponseBody
    @ExceptionHandler(DuplicateKeyException.class)
    public ResultModel<String> duplicateKeyException() {
        log.error("自定义错误代码({}), 错误信息({})", ErrorCode.SQL_ERROR.getValue(), "自增序列重复");
        // 打印错误日志
        return new ResultModel<>(
            ErrorCode.SQL_ERROR.getValue(),
            "数据库自增序列重复，联系技术人员解决！"
        );
    }

    @ResponseBody
    @ExceptionHandler(MyBatisSystemException.class)
    public ResultModel<String> sqlException() {
        log.error("自定义错误代码({}),错误信息({})", ErrorCode.SQL_ERROR.getValue(), "自定义 sql 语句执行异常！");
        // 打印错误日志
        return new ResultModel<>(
            ErrorCode.SQL_ERROR.getValue(),
            "自定义 sql 语句执行异常！"
        );
    }
}
